#include    "unpthread.h"
#include    "ft_info.h"
//#include    "filetrans.h"

#define    IDSIZE    128

static int debug_mode = 0;
static int command_set = 1;
static int admit = 0;

static char toid[IDSIZE];
static char id[IDSIZE];

static ft_info *recv_ft, *send_ft;
static int filesendID = 0;
static int filerecvID = 0;
static int tempID;

pthread_mutex_t recvid_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t sendid_mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t ft_recvList_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t ft_sendList_mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t ft_sendfreeList_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t ft_recvfreeList_mutex = PTHREAD_MUTEX_INITIALIZER;

void console(int command_set, char* id);
void *stdin_do(void *arg);
void *sockin_do(void *arg);
void receiv_file(int connfd);
void* recv_wait(void *arg);
void *file_receiver(void *arg);
void *file_transfer(void *arg);
void debug(char *cont, int debug);
int stdin_parsing(char *str, char *msg, int sockfd);
void sockin_parsing(int sockfd, char* str);
void sig(int sig);

void *
stdin_do(void *arg) {

	int sockfd;
	sockfd = (int) arg;

	char sendline[MAXLINE];
	char msg[MAXLINE];

	(void) signal(SIGTERM, sig);

	pthread_detach(pthread_self());

	for (;;) { // stdin -> send to server
		//console(command_set, id);
		if (fgets(sendline, MAXLINE, stdin) != -1) {
			if (stdin_parsing(sendline, msg, sockfd) != 0) {
				debug(msg, debug_mode);
				writen(sockfd, msg, strlen(msg));
			}
		} else {
			close(sockfd);
			break;
		}
	}

	return (NULL);
}

void *
sockin_do(void *arg) {
	int sockfd;
	sockfd = (int) arg;

	int n;
	char recv_msg[MAXLINE];

	ReadBuffer* rbuf;

	(void) signal(SIGTERM, sig);

	rbuf = createRbuf(MAXLINE);

	pthread_detach(pthread_self());

	while (1) {
		if ((n = readBuf(rbuf, sockfd)) < 0)
			err_sys("read error\n");

		else if (n > 0) {
			while (getMessage(rbuf, recv_msg, MAXLINE) != NULL) {
				debug(recv_msg, debug_mode);
				sockin_parsing(sockfd, recv_msg);
			}
			console(command_set, id);
		}
	}
	return (NULL);
}

int stdin_parsing(char *str, char *msg, int sockfd) {
	char word1[MAXLINE];
	char word2[MAXLINE];
	char word3[MAXLINE];
	char word4[MAXLINE];
	int n, fp;
	memset(msg, 0x00, MAXLINE);
	memset(word1, 0x00, MAXLINE);
	memset(word2, 0x00, MAXLINE);
	memset(word3, 0x00, MAXLINE);
	memset(word4, 0x00, MAXLINE);

	(void) signal(SIGTERM, sig);

	pthread_detach(pthread_self());

	n = sscanf(str, "%s %s %s %s", word1, word2, word3, word4);

	if (((recv_ft = getFTList(1)) != NULL)
			&& (!strcmp(word1, "y") || (!strcmp(word1, "n"))) && (n == 1)) {

		for (; recv_ft != NULL; recv_ft = recv_ft->next) {
			if (recv_ft->state == 0)
				break;
		}
		if (recv_ft == NULL) {
			if (command_set == 1) {
				printf("syntax error\n> ");
				return 0;
			} else
				sprintf(msg, "FROM %s TO %s\r\n%s\r\n\r\n", id, toid, str);
		} else {
			if (!strcmp(word1, "y")) {
				recv_ft->state = 1;
				sprintf(msg, "ACCEPT %s FROM %s TO %s\r\n\r\n",
						recv_ft->filename, recv_ft->component_id,
						recv_ft->my_id);

			} else if (!strcmp(word1, "n")) {
				recv_ft->state = 1;

				pthread_mutex_lock(&ft_recvList_mutex);
				recv_ft = findFT_info(filerecvID, 1);
				pthread_mutex_unlock(&ft_recvList_mutex);

				pthread_mutex_lock(&ft_recvList_mutex);
				pthread_mutex_lock(&ft_recvfreeList_mutex);
				destroyFT_info(recv_ft, 1);
				pthread_mutex_unlock(&ft_recvList_mutex);
				pthread_mutex_unlock(&ft_recvfreeList_mutex);

				pthread_mutex_unlock(&recvid_mutex);

				sprintf(msg, "REJECT %s FROM %s TO %s\r\n\r\n",
						recv_ft->filename, recv_ft->component_id,
						recv_ft->my_id);

			} else {
				printf("Syntax error !\n");
				printf("Are you admit?(Input y/n)\n");
				return 0;
			}
		}
	} else if (command_set == 1) {
		if (!strcmp(word1, "user") && (n == 2)) {
			memset(id, 0x00, IDSIZE);
			sprintf(id, "%s", word2);
			sprintf(msg, "USER %s\r\n\r\n", id);
		} else if (!strcmp(word1, "send") && (n == 4)) { // send file to toid

			if ((fp = open(word2, O_RDWR)) < 0) { // no file
				printf("Error : No file :%s\n> ", word2);
				return 0;
			} else {
				memset(toid, 0x00, IDSIZE);
				strcpy(toid, word4);
				sprintf(msg, "SEND %s FROM %s TO %s\r\n\r\n", word2, id, toid);
			}
		} else if (!strcmp(word1, "quit") && (n == 1)) {
			sprintf(msg, "QUIT\r\n\r\n");
			debug(msg, debug_mode);
			writen(sockfd, msg, strlen(msg));
			Shutdown(sockfd, SHUT_WR); // send FIN
			return 0;
		} else if (!strcmp(word1, "who") && (n == 1)) {
			sprintf(msg, "WHO\r\n\r\n");
		} else if (!strcmp(word1, "help") && (n == 1)) {
			printf("Usage :\n");
			printf("user  <user ID>    -- loggin with <user ID>\n");
			printf("who     -- who are logged in\n");
			printf(
					"talk  [<[peer>|all]    -- start talk session with <peer> or with all the loggined users\n");
			printf(".     -- end of talk session\n");
			printf(
					"send <file name> to <user ID>   -- <file name> file transmission to <user ID> \n");
			printf("help     -- show usage\n");
			printf("debug     -- debug mode on or off (toggle)\n");

			printf("quit     -- quit session\n");

			printf("> ");
			return 0;

		} else if (!strcmp(word1, "debug") && (n == 1)) {
			printf("DEBUG\n");
			if (debug_mode == 0)
				debug_mode = 1;
			else
				debug_mode = 0;
			return 0;
		} else if (!strcmp(word1, "talk") && (n == 2)) {
			printf("%s : ", id);
			memset(toid, 0x00, IDSIZE);
			sprintf(toid, "%s", word2); // word2 is toid
			command_set = 0;
			return 0;
		} else {
			printf("syntax error\n> ");
			return 0;
		}
	} else if (command_set == 0) {
		if (!strcmp(word1, ".")) {
			printf("END of TALK\n> ");
			command_set = 1;
			return 0;
		} else
			sprintf(msg, "FROM %s TO %s\r\n%s\r\n\r\n", id, toid, str);
	}
	return 1;

}

void sockin_parsing(int sockfd, char* str) {
	pthread_t tid;
	ft_info* temp_ft;

	char parsing[MAXLINE];
	char ft_accept[MAXLINE];
	memset(ft_accept, 0x00, MAXLINE);

	memset(parsing, 0x00, MAXLINE);

	char* p1;

	int n, k;

	char fromid[IDSIZE];
	char toid[IDSIZE];
	char message[MAXLINE];
	char buf[MAXLINE];
	char msg[MAXLINE];
	memset(msg, 0x00, MAXLINE);

	char word1[MAXLINE];
	char word2[MAXLINE];
	char word3[MAXLINE];
	char word4[MAXLINE];
	char word5[MAXLINE];
	char word6[MAXLINE];
	char word7[MAXLINE];
	char word8[MAXLINE];
	char word9[MAXLINE];
	char word10[MAXLINE];
	char word11[MAXLINE];
	char word12[MAXLINE];

	(void) signal(SIGTERM, sig);

	n = sscanf(str, "%s %s %s %s %s %s %s %s %s %s %s %s", word1, word2, word3,
			word4, word5, word6, word7, word8, word9, word10, word11, word12);

	if (!strcmp(word1, "230")) {
		printf("SUCCESS LOG IN\n");
	} else if (!strcmp(word1, "221")) {
		printf("Disconnect..\n");
		close(sockfd);
		exit(0);
	} else if (!strcmp(word1, "530")) {
		printf("You are not log in\n");
	} else if (!strcmp(word1, "500")) {
		printf("Syntax error !!!!!!\n");
	} else if (!strcmp(word1, "200")) {
		//printf("complete send message\n");
	} else if (!strcmp(word1, "210")) {
		p1 = space_remover(buf, str, strlen(str));
		p1 = space_remover(buf, p1, strlen(p1)); // p1 = ID list (with white space)
		p1 = space_remover2(p1, strlen(p1)); //p1 = ID list (none space)

		k = strlen(p1);
		*(p1 + (k - 5)) = '\0';

		printf("ID LIST : %s\n", p1);
	} else if (!strcmp(word1, "227")) {
		pthread_mutex_lock(&sendid_mutex);
		filesendID++;
		pthread_mutex_unlock(&sendid_mutex);

		pthread_mutex_lock(&ft_sendList_mutex);
		pthread_mutex_lock(&ft_sendfreeList_mutex);
		send_ft = createFT_info(filesendID, 2); // mutex after plus
		pthread_mutex_unlock(&ft_sendList_mutex);
		pthread_mutex_unlock(&ft_sendfreeList_mutex);

		strcpy(send_ft->address, word2);
		strcpy(send_ft->filename, word8);
		strcpy(send_ft->my_id, word10);
		strcpy(send_ft->component_id, word12);

		pthread_create(&tid, NULL, &file_transfer, send_ft); // send function thread
	} else if (!strcmp(word1, "425")) {
		printf("Receiver reject file transmission\n");

	} else if (!strcmp(word1, "298")) {
		printf(
				"user : %s want to transfer file : %s\nAre you admit?(Input y/n)\n",
				word8, word6);

		temp_ft = malloc(sizeof(ft_info));

		temp_ft->component_id = (char *) malloc((size_t) IDSIZE);
		temp_ft->my_id = (char *) malloc((size_t) IDSIZE);
		temp_ft->filename = (char *) malloc((size_t) MAXLINE);

		strcpy(temp_ft->filename, word6);
		strcpy(temp_ft->component_id, word8);
		strcpy(temp_ft->my_id, word10);

		pthread_create(&tid, NULL, &recv_wait, temp_ft);

		admit = 1;

	} else if (!strcmp(word1, "299")) {
		strcpy(fromid, word5);
		strcpy(toid, word7);

		p1 = space_remover(buf, str, strlen(str));
		p1 = space_remover(buf, p1, strlen(p1));
		p1 = space_remover(buf, p1, strlen(p1));
		p1 = space_remover(buf, p1, strlen(p1));
		p1 = space_remover(buf, p1, strlen(p1));
		p1 = space_remover(buf, p1, strlen(p1));
		p1 = space_remover(buf, p1, strlen(p1));
		p1 = space_remover2(p1, strlen(p1)); //p1 = Message (none space)

		sprintf(message, "%s", p1);
		k = strlen(message);
		*(message + (k - 5)) = '\0';

		printf("%s ) %s\n", fromid, message);

	} else {
		printf("Receive Message error!!\n");
	}
}

void* recv_wait(void *arg) {

	ft_info *p;
	p = ((ft_info *) arg);

	pthread_detach(pthread_self());

	pthread_mutex_lock(&recvid_mutex);
	++filerecvID;
	tempID = filerecvID;

	pthread_mutex_lock(&ft_recvList_mutex);
	pthread_mutex_lock(&ft_recvfreeList_mutex);
	recv_ft = createFT_info(filesendID, 1);
	pthread_mutex_unlock(&ft_recvList_mutex);
	pthread_mutex_unlock(&ft_recvfreeList_mutex);

	recv_ft->ft_id = filerecvID;

	strcpy(recv_ft->filename, p->filename);
	strcpy(recv_ft->component_id, p->component_id);
	strcpy(recv_ft->my_id, p->my_id);

	free(arg);
	return (NULL);

}

void *
file_transfer(void *arg) {
	char sendline[MAXLINE];
	ft_info *p;
	p = ((ft_info *) arg);

	int fp;
	int n;

	if ((fp = open(p->filename, O_RDONLY)) < 0) {
		perror(p->filename);
		return (NULL);
	}

	p->connfd = Tcp_connect(p->address, "9866");

	printf("file transmission start !!\n");
	printf("filename = %s\n", p->filename);
	printf("file transfer to %s\n", p->component_id);

	while ((n = read(fp, sendline, MAXLINE)) > 0) {
		write(p->connfd, sendline, n);
	}

	sleep(10);

	printf("%s file transmission success to %s\n",p->filename,p->component_id);

	pthread_mutex_lock(&ft_sendList_mutex);
	pthread_mutex_lock(&ft_sendfreeList_mutex);
	destroyFT_info(p, 2);
	pthread_mutex_unlock(&ft_sendList_mutex);
	pthread_mutex_unlock(&ft_sendfreeList_mutex);

	close(p->connfd);
	return (NULL);

}

void receiv_file(int connfd) {
	pthread_t tid;

	pthread_mutex_lock(&ft_recvList_mutex);
	recv_ft = findFT_info(tempID, 1);
	pthread_mutex_unlock(&ft_recvList_mutex);


	pthread_mutex_unlock(&recvid_mutex);

	recv_ft->connfd = connfd;

	pthread_create(&tid, NULL, &file_receiver, recv_ft);

}

void *
file_receiver(void *arg) {
	char recvbuf[MAXLINE];
	ft_info *p;

	p = ((ft_info *) arg);
	int fp;
	int n, i;

	pthread_detach(pthread_self());

	for (i = 1;; i++) {
		if ((fp = open(p->filename, O_RDWR)) < 0) {
			fp = open(p->filename, O_CREAT | O_RDWR, 0664);
			break;
		}
//		sscanf(recv_ft->filename, "%s(copy)", recv_ft->filename);
		sprintf(p->filename, "%s.copy", p->filename);
	}

	printf("file receive start !!\n");
	printf("filename : %s\n", p->filename);
	printf("file receive from %s\n", p->component_id);

	while ((n = read(p->connfd, recvbuf, MAXLINE)) > 0) {
		write(fp, recvbuf, n);
	}

	printf("%s file receive success from %s\n", p->filename,p->component_id);

	pthread_mutex_lock(&ft_recvList_mutex);
	pthread_mutex_lock(&ft_recvfreeList_mutex);
	destroyFT_info(p, 1);
	pthread_mutex_unlock(&ft_recvList_mutex);
	pthread_mutex_unlock(&ft_recvfreeList_mutex);

	close(p->connfd);

	return (NULL);

}

void console(int command_set, char* id) {
	if (command_set == 1)
		printf("> ");
	else if (command_set == 0)
		printf("%s : ", id);

	fflush(stdout);
}

void debug(char *cont, int debug) {
	if (debug == 1) {
		write(fileno(stderr), cont, strlen(cont));
		printf("\n");
	}
}
void sig(int sig) {
	fprintf(stderr, "\nsignal SIGTERM: program close");

}
